home *** CD-ROM | disk | FTP | other *** search
/ Just Call Me Internet / Just Call Me Internet.iso / prog / atari / c / nos042_s / nntpcli.c < prev    next >
C/C++ Source or Header  |  1994-09-16  |  67KB  |  2,367 lines

  1. /*
  2.  *    Client routines for Network News Tranfer Protocol ala RFC977
  3.  *
  4.  *    Copyright 1990 Anders Klemets - SM0RGV, All Rights Reserved.
  5.  *    Permission granted for non-commercial copying and use, provided
  6.  *    this notice is retained.
  7.  *
  8.  *    Changes copyright 1990 Bernie Roehl, All Rights Reserved.
  9.  *    Permission granted for non-commercial copying and use, provided
  10.  *    this notice is retained.
  11.  *
  12.  *  Revision history:
  13.  *
  14.  *     May 11, 1990 - br checked for invalid chars in news filenames
  15.  *
  16.  *     May 10, 1990 - br changed date stamp in 'From ' lines to
  17.  *            seconds since GMT (to make parsing and expiry easier)
  18.  *
  19.  *     May 9, 1990 - br added locking of nntp.dat and history files,
  20.  *            second parameter to NNTP DIR, fixed bug in updating of
  21.  *            nntp.dat
  22.  *
  23.  *     early May, 1990 -- br added NNTP TRACE, NNTP DIR,
  24.  *            server-specific newsgroups and connection windows,
  25.  *            locking of newsgroup files using mlock() and rmlock(),
  26.  *            date stamping of 'From ' lines, increased stack space,
  27.  *            updating of nntp.dat only on successful sessions.
  28.  *
  29.  *     July 19, 1990 pa0gri Delinted and cleaned up. (calls and includes)
  30.  *
  31.  *    20 May 92    1.2        GT    Output file in "snews" batch format.
  32.  *    21 May 92    1.3        GT    Fix 1.2.
  33.  *    07 Jun 92    1.4        GT    "connect_wait_val" timeout on connect () call.
  34.  *    11 Jun 92    1.5        GT    Booked out in error.
  35.  *    15 Jun 92    1.6        GT    Flush history file write.
  36.  *
  37.  *    21-Jul-92    Chris Sowden (csowden@mouse.demon.co.uk)
  38.  *                    Added NNTP BATCH and NNTP VERBOSE commands.
  39.  *                    Re-wrote article fetching section to allow overlapped
  40.  *                    operation (batch mode).
  41.  *                    Added a 64K bit history hash table.
  42.  *                    Added 2 mins time adjustment to allow for a fast clock.
  43.  *                    Added status message showing articles, bytes and rate.
  44.  *                    Lock "nntp.dat" for duration of session to prevent races.
  45.  *                    Numerous changes to speed things up.
  46.  *
  47.  *    08 Aug 92    1.8        MM    Force disc writes of history file.
  48.  *
  49.  *    11-Sep-92    Chris Sowden (csowden@mouse.demon.co.uk)
  50.  *                    Look for newsgroup list in data file first.
  51.  *                    Allow multiple NEWNEWS requests.
  52.  *                    Added NNTP NEWGROUPS command.
  53.  *                    Write directly to batch file in newsbatch mode.
  54.  *                    Additional messages in verbose mode.
  55.  *                    Remove zero length files.
  56.  *
  57.  *    14 Sep 92    GT    Use GMT form of NEWNEWS and NEWGROUPS commands.
  58.  *
  59.  *    14-Sep-92    Chris Sowden (csowden@mouse.demon.co.uk)
  60.  *                    Added counting to verbose received mesage.
  61.  *
  62.  *    15 Sep 92    Chris Sowden (csowden@mouse.demon.co.uk)
  63.  *                    Fixed reentrancy problem.
  64.  *
  65.  *    15 Sep 92    GT    Fixed race condition on GMT vs. local time reading
  66.  *                    between news and mail.
  67.  *                    
  68.  * 13-Nov-92    Chris Sowden (csowden@mouse.demon.co.uk)
  69.  *                    Added SAFETY command, expanded BATCH command
  70.  *                    Uses child processes for initialisation and requests
  71.  *                    New KILL file and GET file for selective article fetching
  72.  *                    Uses correct "#! rnews nnnn" style newsbatch separator
  73.  *                    Improved history file handling (especially for duplicates)
  74.  *                    Added progress messages/statistics in verbose mode
  75.  *                    Now logs all error messages and news summary
  76.  *                    
  77.  * 01-Dec-92    Chris Sowden (csowden@mouse.demon.co.uk)
  78.  *                    Improved the opening sequence of KILL and GET files
  79.  *                    Corrected error levels of file opens
  80.  *                    Close temporary new message file
  81.  *                    Tried to reduce likelihood of lock files being left around
  82.  *                    Removed extra linefeed from batch files
  83.  *
  84.  *    08 May 93    1.13    GT    Fix warnings.
  85.  *                            Reinstate 2 minute fudge factor.
  86.  *                            Drop the word "article" from the "news
  87.  *                            arrived" message.
  88.  *                            Fix signed variables problem.
  89.  *
  90.  * 07 Nov 93    1.14    GT    Display NEWNEWS results when "nntp verb on".
  91.  *                         Fix dot transparency. Fix blank lines in error log.
  92.  * 
  93.  * $Id: nntpcli.c 1.14 94/01/04 14:09:53 ROOT_DOS Exp $
  94.  *
  95.  * -------------------------------------------------------------------------
  96.  *
  97.  * Atari ST NOS Version by David Nash - dnash@chaos.demon.co.uk
  98.  *
  99.  * 16.08.94 - Removed Local profile code, mail style message batches,
  100.  *            nntp_reg call
  101.  * 25.07.94 - Improve status messages in donntpstat,
  102.  *            Local profile code conditional on NNTP_PROF (default disabled)
  103.  * 03.07.94 - response >= 400 now trace level 1 (from level 2)
  104.  * 05.06.94 - newsbatch default changed to output in rnews fromat
  105.  * 05.08.93 - articles for posting now suffixed .nwk and new format
  106.  * 09.05.93 - added prototypes for __regargs 
  107.  * 18.04.93 - rnews batched or unbatched format now selectable 
  108.  * 09.04.93 - added donnstatus to report the status of a nntp session
  109.  * 10.03.93 - added Profile and Post subcommands from ANOS version by G1YYH
  110.  * 18.07.93 - post articles from Newsqdir, suffix now .nws. Fixed nntplist
  111.  *
  112.  * -------------------------------------------------------------------------
  113.  */
  114.  
  115. #include <sys/types.h>
  116. #include <sys/stat.h> /* ST NOS */
  117. #include <time.h>
  118. #ifndef ATARI             /* ST NOS */
  119. #include <sys/timeb.h>
  120. #endif
  121. #include <ctype.h>
  122. #include <string.h>  /* for strchr() */
  123. #include <io.h>
  124. #ifdef MSDOS
  125. #include <dir.h>
  126. #endif
  127. #include "global.h"
  128. #include "timer.h"
  129. #include "cmdparse.h"
  130. #include "commands.h"
  131. #include "socket.h"
  132. #include "usock.h"
  133. #include "netuser.h"
  134. #include "proc.h"
  135. #include "smtp.h"
  136. #include "files.h"
  137. #include "session.h" /* ST NOS */
  138. #include "main.h" /* for main_exit */
  139.  
  140. void     filedir(char *, int, char *);        /* from filedir.c                            */
  141.  
  142. /* The grouplist must fit on a line like "NEWNEWS <grouplist> 000000 000000 GMT\n" */
  143.  
  144. #define NNTPMAXLEN    512
  145. #define NNTPMAXGROUPLIST NNTPMAXLEN-28
  146.  
  147. static struct nntpservers {
  148.     struct timer nntpcli_t;
  149.     char *name;
  150.     char *groups;
  151.     int lowtime, hightime;  /* for connect window */
  152.     struct nntpservers *next;
  153. };
  154.  
  155. struct textline {
  156.     struct textline *nextline;                    /* pointer to next text line         */
  157.     char text[NNTPMAXLEN];                        /* array holding text                  */
  158. };
  159.  
  160. struct    nntprequest;                            /* hide nntprequest struct tag    */
  161.  
  162. #define    NULLTEXTLINE (struct textline *) 0
  163. #define    MINTEXTLINESIZE (sizeof(struct textline)-NNTPMAXLEN)
  164.  
  165. #define    NULLNNTP    (struct nntpservers *)NULL
  166.  
  167. #define    NEWSBATCH TRUE                            /* output for batching newsreader */
  168. #define    NEWSBATCHFORMAT "#! rnews %010lu\n"
  169.  
  170. static int newsbatch = 1;                        /* output for offline unbatch        */
  171.  
  172. struct nntpservers *Nntpservers = NULLNNTP;
  173. static char *Nntpgroups = NULLCHAR;
  174. static unsigned short nntptrace = 1;
  175. static int nntpbatch         = 0;
  176. static int nntpbatchsize     = 2;
  177. static int nntpnewgroups     = 0;
  178. static int nntpsafety         = 1;
  179. static int nntpverbose         = 1;
  180.  
  181. static char *validchars = "abcdefghijklmnopqrstuvwxyz0123456789-_";
  182.  
  183. ext_dokicks(void);
  184. static void nntptick(void *tp);
  185.  
  186. static int dobatch(int argc,char *argv[],void *p);
  187. static int doadds(int argc,char *argv[],void *p);
  188. static int dondir(int argc,char *argv[],void *p);
  189. static int dodrops(int argc,char *argv[],void *p);
  190. static int dogroups(int argc,char *argv[],void *p);
  191. static int dokicks(int argc,char *argv[],void *p);
  192. static int dolists(int argc,char *argv[],void *p);
  193. static int donewgroups(int argc,char *argv[],void *p);
  194. static int dosafety(int argc,char *argv[],void *p);
  195. static int donntrace(int argc,char *argv[],void *p);
  196. static int doverbose(int argc,char *argv[],void *p);
  197.  
  198. static int donnstatus(int argc,char *argv[],void *p);
  199. static int donntplist(int argc,char *argv[],void *p);
  200. static int donntpkill(int argc,char *argv[],void *p);
  201.  
  202. static void error(int level,char *text);
  203.  
  204. #ifdef NEVER
  205. static void freetextlinelist(struct textline **list);
  206. #endif
  207.  
  208. static void createkilllists(void);
  209. static void scankilllists(char *line,int *status);
  210.  
  211. static unsigned int hashmsgid(char *msgid,unsigned int modulus);
  212. static void createhashtable(void);
  213. static int isinhashtable(char *msgid);
  214. static int isinhistory(char *msgid);
  215. static void addtohashtable(char *msgid);
  216. static void addtohistory(char *msgid);
  217.  
  218. static int stripgroups(char *s);
  219. static unsigned long unitspersecond(unsigned long units,unsigned long milliseconds);
  220.  
  221. #ifdef NEVER
  222. static int getreply(void);
  223. static int getheader(struct nntprequest *request);
  224. static void moveheadertofile(FILE *fp,struct nntprequest *request);
  225. static int gettxt(FILE *fp,struct nntprequest *request, int newnews);
  226. static int putarticle(FILE *msgf,long int *msgfpos,struct nntprequest *request);
  227. #endif
  228.  
  229. static void __stdargs nntp_init(int argc,void *argv,void *parentproc);
  230. static void __stdargs nntp_send(int argc,void *argv,void *parentproc);
  231. static void __stdargs nntp_main(int i1,void *tp,void *v1);
  232.  
  233. static void freetextlinelist(struct textline **);
  234. static void createkilllists(void);
  235. static void createhashtable(void);
  236. static int  getreply(void);
  237. static int  getheader(struct nntprequest *);
  238. static void moveheadertofile(FILE *, struct nntprequest *);
  239. static int  putarticle(FILE *, long int *, struct nntprequest *);
  240. static void nntp_reg (void);
  241.  
  242. /* Tracing levels:
  243.     0 - no tracing
  244.     1 - serious errors reported
  245.     2 - transient errors reported
  246.     3 - session progress reported
  247.     4 - actual received articles displayed
  248.  */
  249.  
  250. static struct cmds Nntpcmds[] = {
  251.     { "addserver",        doadds,        0,        3,    "nntp addserver <nntpserver> <interval>"},
  252.     { "batch",            dobatch,        0,        0,    NULLCHAR},
  253.     { "directory",        dondir,        0,        0,    NULLCHAR},
  254.     { "dropserver",    dodrops,        0,        2, "nntp dropserver <nntpserver>"},
  255.     { "groups",            dogroups,    0,        0,    NULLCHAR},
  256.     { "kick",            dokicks,        0,        0, "nntp kick <nntpserver>"},
  257.    { "kill",            donntpkill,    0,     2, "kill <jobnumber>"},
  258.     { "listservers",    dolists,        0,        0,    NULLCHAR},
  259.    { "messages",        donntplist,    0,     0, NULLCHAR},
  260.     { "newgroups",        donewgroups,0,     0, NULLCHAR},
  261.     { "safety",            dosafety,    0,        0,    NULLCHAR},
  262.     { "status",            donnstatus,    0,        0,    NULLCHAR},
  263.     { "trace",            donntrace,    0,        0,    NULLCHAR},
  264.     { "verbose",        doverbose,    0,        0,    NULLCHAR},
  265.     { NULLCHAR },
  266. };
  267.  
  268. int donntp(int argc, char *argv[], void *p)
  269. {
  270.     return subcmd(Nntpcmds,argc,argv,p);
  271. }
  272.  
  273. static int doadds(int argc, char *argv[], void *p)
  274. {
  275.     struct nntpservers *np;
  276.  
  277.     for (np = Nntpservers; np != NULLNNTP; np = np->next)
  278.         if (stricmp(np->name, argv[1]) == 0)
  279.             break;
  280.  
  281.     if (np == NULLNNTP) {
  282.         np = (struct nntpservers *) callocw(1,sizeof(struct nntpservers));
  283.         np->name = strdup(argv[1]);
  284.         np->next = Nntpservers;
  285.         Nntpservers = np;
  286.         np->groups = NULLCHAR;
  287.         np->lowtime = np->hightime = -1;
  288.         np->nntpcli_t.func = nntptick;    /* what to call on timeout */
  289.         np->nntpcli_t.arg = (void *)np;
  290.     }
  291.     
  292.     if (argc > 3) {
  293.         int i;
  294.         if (np->groups == NULLCHAR) {
  295.             np->groups = mallocw(NNTPMAXLEN);
  296.             *np->groups = '\0';
  297.         }
  298.         for (i = 3; i < argc; ++i) {
  299.             if (isdigit(*argv[i])) {
  300.                 int lh, ll, hh, hl;
  301.                 sscanf(argv[i], "%d:%d-%d:%d", &lh, &ll, &hh, &hl);
  302.                 np->lowtime = lh * 100 + ll;
  303.                 np->hightime = hh * 100 + hl;
  304.             } else if ((strlen(np->groups)+1+strlen(argv[i])) > NNTPMAXGROUPLIST)
  305.                 tprintf("Group list too long!  Group '%s' ignored!\n", argv[i]);
  306.             else {  /* it's a group, and it fits... add it to list */
  307.                 if (*np->groups != '\0')
  308.                     strcat(np->groups, ",");
  309.                 strcat(np->groups, argv[i]);
  310.             }
  311.         }
  312.         if (*np->groups == '\0') {    /* No groups specified? */
  313.             free(np->groups);
  314.             np->groups = NULLCHAR;
  315.         }
  316.     }
  317.     /* set timer duration */
  318.     set_timer(&np->nntpcli_t,atol(argv[2])*1000L);
  319.     start_timer(&np->nntpcli_t);        /* and fire it up */
  320.     return 0;
  321. }
  322.  
  323. static int dodrops(int argc, char *argv[], void *p)
  324. {
  325.     struct nntpservers *np, *npprev = NULLNNTP;
  326.  
  327.     for (np = Nntpservers; np != NULLNNTP; npprev = np, np = np->next)
  328.         if (stricmp(np->name,argv[1]) == 0) {
  329.             stop_timer(&np->nntpcli_t);
  330.             free(np->name);
  331.             if (np->groups)
  332.                 free(np->groups);
  333.             if(npprev != NULLNNTP)
  334.                 npprev->next = np->next;
  335.             else
  336.                 Nntpservers = np->next;
  337.             free((char *)np);
  338.             return 0;
  339.     }
  340.     tprintf("No such server enabled\n");
  341.     return 0;
  342. }
  343.  
  344.  
  345. static int dolists(int argc, char *argv[], void *p)
  346. {
  347.     struct nntpservers *np;
  348.     
  349.     for (np = Nntpservers; np != NULLNNTP; np = np->next) {
  350.         char tbuf[80];
  351.         if (np->lowtime != -1 && np->hightime != -1)
  352.             sprintf(tbuf, " -- %02d:%02d-%02d:%02d", np->lowtime/100, np->lowtime%100, np->hightime/100, np->hightime%100);
  353.         else
  354.             tbuf[0] = '\0';
  355.         tprintf("%-32s (%lu/%lu%s) %s\n", np->name,
  356.             read_timer(&np->nntpcli_t) /1000L,
  357.             dur_timer(&np->nntpcli_t) /1000L,
  358.             tbuf, np->groups ? np->groups : "");
  359.     }
  360.     return 0;
  361. }
  362.  
  363.  
  364. static int donntrace(int argc, char *argv[], void *p)
  365. {
  366.     return setshort(&nntptrace, "NNTP tracing", argc, argv);
  367. }
  368.     
  369. static char *News_spool = NULL;
  370. static int np_all = 0;  /* non-zero if Newsdir is a malloc'ed space */
  371.  
  372. static int dondir(int argc, char *argv[], void *p)
  373. {
  374.     if (argc < 2) {
  375.         tprintf("spool: %s\n", News_spool ? News_spool : Mailspool);
  376.         tprintf("control: %s\n", Newsdir);
  377.     } else {
  378.         char *p;
  379.         if ((p = strchr(argv[1], '=')) != NULLCHAR) {  /* set a groupdir */
  380.                 tprintf("Directory table not supported\n");
  381.         }
  382.         else {  /* no '=', so just set default */
  383.             if (News_spool)
  384.                 free(News_spool);
  385.             News_spool = strdup(argv[1]);
  386.         }
  387.  
  388.         if (argc > 2) {  /* they specified a newsdir as well */
  389.             if (np_all)
  390.                 free(Newsdir);
  391.             Newsdir = strdup(argv[2]);
  392.             np_all = 1;
  393.         }
  394.     }
  395.     return 0;
  396. }
  397.  
  398.  
  399. int ext_dokicks(void)
  400. {
  401.     char *args[] = { "" };
  402.      
  403.      dokicks(1, args, NULL);
  404.     return 0;
  405. }
  406.  
  407.  
  408. static int dokicks(int argc, char *argv[], void *p)
  409. {
  410.     struct nntpservers *np;
  411.  
  412.     for (np = Nntpservers; np != NULLNNTP; np = np->next)
  413.         if (argc == 1 || stricmp(np->name,argv[1]) == 0) {
  414.             /* If the timer is not running, the timeout function has
  415.             * already been called and we don't want to call it again.
  416.             */
  417.             if(run_timer(&np->nntpcli_t)) {
  418.                 stop_timer(&np->nntpcli_t);
  419.                 nntptick((void *)np);
  420.             }
  421.             if (argc != 1)
  422.                 return 0;
  423.     }
  424.     if (argc != 1)
  425.         tprintf("No such server enabled.\n");
  426.     return 0;
  427. }
  428.  
  429.  
  430. static int dogroups(int argc, char *argv[], void *p)
  431. {
  432.     char grouplist[NNTPMAXLEN];
  433.     int i;
  434.  
  435.     if (argc < 2) {
  436.         if(Nntpgroups == NULLCHAR || (Nntpgroups != NULLCHAR && strcmp(Nntpgroups,"*") == 0))
  437.             tprintf("All groups are currently enabled\n");
  438.         else
  439.             tprintf("Currently enabled newsgroups:\n%s\n",Nntpgroups);
  440.         return 0;
  441.     }
  442.     if (Nntpgroups != NULLCHAR)
  443.         free(Nntpgroups);
  444.  
  445.     *grouplist = '\0';
  446.  
  447.     for (i=1; i < argc; ++i) {
  448.         if ((strlen(grouplist)+1+strlen(argv[i])) > NNTPMAXGROUPLIST)
  449.             tprintf("Group list too long!  Group '%s' ignored!\n", argv[i]);
  450.         else {
  451.             if(i > 1)
  452.                 strcat(grouplist,",");
  453.             strcat(grouplist,argv[i]);
  454.         }
  455.     }
  456.     Nntpgroups = strdup(grouplist);
  457.     return 0;
  458. }
  459.  
  460.  
  461. static int dobatch(int argc, char *argv[], void *p)
  462. {
  463.     if (argc < 2) {
  464.         tprintf("NNTP batch mode: %s, buffers: %d\n",nntpbatch ? "on":"off",nntpbatchsize);
  465.         return 0;
  466.     }
  467.     if (argc > 2) {
  468.         nntpbatchsize = atoi(argv[2]);
  469.     }
  470.     return setbool(&nntpbatch,"NNTP batch mode",argc,argv);
  471. }
  472.  
  473.  
  474. static int dosafety(int argc, char *argv[], void *p)
  475. {
  476.     return setbool(&nntpsafety, "NNTP safety mode", argc, argv);
  477. }
  478.  
  479.  
  480. static int doverbose(int argc, char *argv[], void *p)
  481. {
  482.     return setbool(&nntpverbose, "NNTP verbose mode", argc, argv);
  483. }
  484.  
  485.  
  486. static int donewgroups(int argc, char *argv[], void *p)
  487. {
  488.     return setbool(&nntpnewgroups, "NNTP get new groups", argc, argv);
  489. }
  490.  
  491.  
  492. /*
  493.     nntptick - This is the routine that gets called every so often to connect to
  494.               NNTP servers.
  495. */
  496.  
  497. static void nntptick(void *tp)
  498. {
  499.     newproc("NNTP client main", 3072, nntp_main, 0, tp, NULL, 0);
  500. }
  501.  
  502. struct nntprequest {
  503.     char         msgid[NNTPMAXLEN];            /* id of requested header/body/article */
  504.     struct     textline *header;                /* header text */
  505.     unsigned int lines;                        /* lines in header/body/article */
  506.     unsigned long chars;                        /* characters in header/body/article */
  507.     enum {
  508.         reqempty = 0,                            /* vacant request */
  509.         reqgetarticle,                            /* article from get file */
  510.         reqnewarticle,                            /* article from new file */
  511.         reqheader,                                /* header only */
  512.         reqbody,                                    /* body to accompany header */
  513.         reqkill,                                    /* kill this header */
  514.         reqkeep,                                    /* get a body to accompany this header */
  515.         reqend,                                    /* no more requests */
  516.         reqquit                                    /* close connection */
  517.     } status;
  518.     struct nntprequest *nextrequest;        /* pointer to next request */
  519. };
  520.  
  521. #define NULLNNTPREQUEST (struct nntprequest *) 0
  522.  
  523. struct nntpsession {
  524.     int s;                                        /* NNTP socket */
  525.  
  526.     struct proc *childproc;                    /* child process, either init or send */
  527.  
  528.     FILE *getmsgf;                                /* file holding messages to get */
  529.     FILE *newmsgf;                                /* temporary file for NEWNEWS response */
  530.     FILE *historyf;                            /* history file */
  531.     unsigned char *hashtable;                /* history hash table */
  532.  
  533.     struct nntprequest
  534.         *requesthead,                            /* request pointer used by sender */
  535.         *requesttail;                            /* request pointer used by receiver */
  536.  
  537.     struct textline
  538.         *killlist,                                /* kill patterns */
  539.         *keeplist;                                /* keep patterns */
  540.  
  541.     int quit;                                    /* if true quit immediately */
  542.  
  543.     unsigned int historyentries;            /* number of entries in history file */
  544.     unsigned int historyscans;                /* number of complete history file scans */
  545.     unsigned int getarticles;                /* number of lines in get file */
  546.     unsigned int getinvalid;                /* number of invalid ids from get file */
  547.     unsigned int getunavailable;            /* number of articles from get file unavailable */
  548.     unsigned int getreceived;                /* number of complete articles received */
  549.     unsigned int newarticles;                /* number of new article message ids received */
  550.     unsigned int newduplicates;            /* number of duplicate articles */
  551.     unsigned int newunavailable;            /* number of articles unavailalable */
  552.     unsigned int newheadersreceived;        /* number of headers only received */
  553.     unsigned int newreceived;                /* number of complete articles received */
  554.     unsigned int groupsreceived;            /* number of new groups received */
  555.     unsigned long receivedarticlechars;    /* number of characters in articles received */
  556.     unsigned long receivednewschars;        /* number of characters in news altogether */
  557.     unsigned long receivedtotalchars;    /* number of characters received altogether */
  558.     unsigned long receivedheaderchars;    /* total number of header characters */
  559.     unsigned long startsession;            /* clock value at start of session */
  560.     unsigned long startarticles;            /* clock value at start of article fetch */
  561.     unsigned long endsession;                 /* clock value at end of session */
  562. };
  563.  
  564. #define NULLNNTPSESSION (struct nntpsession *) 0
  565.  
  566. static struct nntpsession *nntp = NULLNNTPSESSION;
  567.  
  568. static void error(int level, char *text)
  569. {
  570.     char *p;                            /* -> text                            */
  571.  
  572.     p = mallocw(strlen (text) + 1);
  573.     (void) strcpy(p, text);
  574.     rip(p);
  575.  
  576.     if (level <= 2)
  577.         log(nntp->s, "%s", p);
  578.  
  579.     if (level <= nntptrace || nntpverbose != 0)
  580.         tprintf("%s\n", p);
  581.  
  582.     free (p);
  583. }
  584.  
  585.  
  586. static void
  587. freetextlinelist(list)
  588. struct textline **list;
  589. {
  590.     struct textline *lastline, *thisline = *list;
  591.  
  592.     while (thisline != NULLTEXTLINE) {
  593.         lastline = thisline;
  594.         thisline = thisline->nextline;
  595.         free(lastline);
  596.     }
  597.     *list = NULLTEXTLINE;
  598. }
  599.  
  600.  
  601. static void
  602. createkilllists()
  603. {
  604.     FILE *killf;
  605.     char buf[NNTPMAXLEN], *s, *d;
  606.     int keep, n, keeplines = 0, killlines = 0;
  607.     struct textline *newline, *lastkillline = NULLTEXTLINE, *lastkeepline = NULLTEXTLINE;    
  608.  
  609.     /* If it exists, open the kill file */
  610.  
  611.     sprintf(buf, "%s/kill", Newsdir);
  612.     
  613.     if (access(buf, 0) == 0) {
  614.  
  615.         if (mlock(Newsdir, "kill")) {
  616.             sprintf(buf, "NNTP Can't lock file %s/kill", Newsdir);
  617.             error(2, buf);
  618.             nntp->quit = TRUE;
  619.             return;
  620.         }
  621.         
  622.         if ((killf = fopen(buf, READ_TEXT)) == NULLFILE) {
  623.             sprintf(buf, "NNTP Can't open file %s/kill", Newsdir);
  624.             error(1, buf);
  625.             rmlock(Newsdir, "kill");
  626.             nntp->quit = TRUE;
  627.             return;
  628.         }
  629.  
  630.         while (fgets(buf, NNTPMAXLEN, killf) != NULLCHAR) {
  631.  
  632.             /* strip out extra characters and force lower case */
  633.             s = d = buf;
  634.             n = 0;
  635.             keep = (*s == '!');
  636.             if (keep)
  637.                 s++;
  638.             while (*s != '\n' && *s != '\0') {
  639.                 if (isspace(*s)) {
  640.                     do s++; while (isspace(*s));
  641.                     *d++ = ' ';
  642.                 } else {
  643.                     *d++ = tolower(*s++);
  644.                 }
  645.                 n++;
  646.             }
  647.             *d = '\0';
  648.  
  649.             if ((newline = (struct textline *) malloc(MINTEXTLINESIZE+n+1)) == NULLTEXTLINE) {
  650.                 error(2, "NNTP Can't allocate kill buffer");
  651.                 fclose(killf);
  652.                 rmlock(Newsdir, "kill");
  653.                 nntp->quit = TRUE;
  654.                 return;
  655.             }
  656.             newline->nextline = NULLTEXTLINE;
  657.             memcpy(newline->text, buf, n+1);
  658.  
  659.             if (keep) {
  660.                 if (lastkeepline == NULLTEXTLINE)
  661.                     nntp->keeplist = newline;
  662.                 else
  663.                     lastkeepline->nextline = newline;
  664.                 lastkeepline = newline;
  665.                 keeplines++;
  666.             } else {
  667.                 if (lastkillline == NULLTEXTLINE)
  668.                     nntp->killlist = newline;
  669.                 else
  670.                     lastkillline->nextline = newline;
  671.                 lastkillline = newline;
  672.                 killlines++;
  673.             }
  674.  
  675.             if (!nntp->quit && !main_exit)
  676.                 pwait(NULL);
  677.         }
  678.         if (nntptrace >= 3)
  679.             tprintf("NNTP Kill file read: %d kill lines, %d keep lines\n",killlines,keeplines);
  680.  
  681.         fclose(killf);
  682.         rmlock(Newsdir, "kill");
  683.     }
  684. }
  685.  
  686.  
  687. static void
  688. scankilllists(line,status)
  689. char *line;
  690. int *status;
  691. {
  692.     char buf[NNTPMAXLEN], *s, *d;
  693.     struct textline *thisline;
  694.  
  695.     /* strip space and convert to lower case */
  696.     s = line;
  697.     d = buf;
  698.     while (*s != '\n' && *s != '\0') {
  699.         if (isspace(*s)) {
  700.             do s++; while (isspace(*s));
  701.             *d++ = ' ';
  702.         } else {
  703.             *d++ = tolower(*s++);
  704.         }
  705.     }
  706.     *d = '\0';
  707.  
  708.     thisline = nntp->keeplist;
  709.     while (thisline != NULLTEXTLINE) {
  710.         if (wildmat(buf,thisline->text,NULLCHARP)) {
  711.             if (nntptrace >= 3)
  712.                 tprintf("Keep: %s\n      %s\n",buf,thisline->text);
  713.             *status = reqkeep;
  714.             return;
  715.         }
  716.         thisline = thisline->nextline;
  717.     }
  718.     if (*status != reqkill) {
  719.         thisline = nntp->killlist;
  720.         while (thisline != NULLTEXTLINE) {
  721.             if (wildmat(buf,thisline->text,NULLCHARP)) {
  722.                 if (nntptrace >= 3)
  723.                     tprintf("Kill: %s\n      %s\n",buf,thisline->text);
  724.                 *status = reqkill;
  725.                 return;
  726.             }
  727.             thisline = thisline->nextline;
  728.         }
  729.     }
  730. }
  731.  
  732.  
  733. #define HASH1 32749
  734. #define HASH2 32719
  735.  
  736. unsigned int
  737. hashmsgid(msgid, modulus)
  738. char *msgid;
  739. unsigned int modulus;
  740. {
  741.     unsigned long int i = 0;
  742.  
  743.     while (*msgid != '\0') {
  744.         do i = (i<<8)|*msgid++; while (*msgid != '\0' && i < (1L<<24));
  745.         i %= modulus;
  746.     }
  747.     return (unsigned int) i;
  748. }
  749.  
  750.  
  751. static void
  752. createhashtable()
  753. {
  754.     char msgid[NNTPMAXLEN];
  755.     unsigned char *hashtable = NULL;
  756.     unsigned int i, lines = 0, collisions = 0;
  757.     int found;
  758.  
  759.     /* If there is sufficient memory then build a 64K bit hash table from
  760.      *  the history file.
  761.      */
  762.  
  763.     if (availmem() > Memthresh+(8192*sizeof(unsigned char)) &&
  764.             (hashtable = (unsigned char *) calloc(8192,sizeof(unsigned char))) != NULL) {
  765.         rewind(nntp->historyf);
  766.         while (fgets(msgid,NNTPMAXLEN,nntp->historyf) != NULL) {
  767.             lines++;
  768.             found = 1;
  769.             i = hashmsgid(msgid,HASH1);
  770.             if (!(hashtable[i/4]&(0x01<<(i%4)))) {
  771.                 hashtable[i/4] |= 0x01<<(i%4);
  772.                 found = 0;
  773.             }
  774.             i = hashmsgid(msgid,HASH2);
  775.             if (!(hashtable[i/4]&(0x10<<(i%4)))) {
  776.                 hashtable[i/4] |= 0x10<<(i%4);
  777.                 found = 0;
  778.             }
  779.             if (found)
  780.                 collisions++;
  781.             if (!nntp->quit && !main_exit)
  782.                 pwait(NULL);
  783.         }
  784.         rewind(nntp->historyf);
  785.         if (nntptrace >= 3)
  786.             tprintf("NNTP Hash table created: %u ids, %u collisions\n", lines, collisions);
  787.     }
  788.     nntp->hashtable = hashtable;
  789.     nntp->historyentries = lines;
  790.     nntp->historyscans = 1;
  791. }
  792.  
  793.  
  794. static int
  795. isinhashtable(msgid)
  796. char *msgid;
  797. {
  798.     unsigned int i, j;
  799.  
  800.     if (nntp->hashtable == NULL)
  801.         return 1;
  802.     else {
  803.         i = hashmsgid(msgid,HASH1);
  804.         j = hashmsgid(msgid,HASH2);
  805.         if ((nntp->hashtable[i/4]&(0x01<<(i%4))) &&
  806.              (nntp->hashtable[j/4]&(0x10<<(j%4))))
  807.             return 1;
  808.     }
  809.     return 0;
  810. }
  811.  
  812.  
  813. static int
  814. isinhistory(msgid)
  815. char *msgid;
  816. {
  817.     char historyid[NNTPMAXLEN];
  818.     long historyfpos;
  819.  
  820.     if (isinhashtable(msgid)) {
  821.         historyfpos = ftell(nntp->historyf);
  822.         while(fgets(historyid,NNTPMAXLEN,nntp->historyf) != NULLCHAR) {
  823.             if(strcmp(historyid,msgid) == 0)
  824.                 return 1;
  825.             if (!nntp->quit && !main_exit)
  826.                 pwait(NULL);
  827.         }
  828.         nntp->historyscans++;
  829.         rewind(nntp->historyf);
  830.         while(fgets(historyid,NNTPMAXLEN,nntp->historyf) != NULLCHAR) {
  831.             if(strcmp(historyid,msgid) == 0)
  832.                 return 1;
  833.             if (ftell(nntp->historyf) >= historyfpos)
  834.                 break;
  835.             if (!nntp->quit && !main_exit)
  836.                 pwait(NULL);
  837.         }
  838.     }
  839.     return 0;
  840. }
  841.     
  842.  
  843. static void
  844. addtohashtable(msgid)
  845. char *msgid;
  846. {
  847.     unsigned int i, j;
  848.  
  849.     if (nntp->hashtable != NULL) {
  850.         i = hashmsgid(msgid,HASH1);
  851.         nntp->hashtable[i/4] |= 0x01<<(i%4);
  852.         j = hashmsgid(msgid,HASH2);
  853.         nntp->hashtable[j/4] |= 0x10<<(j%4);
  854.     }
  855. }
  856.  
  857.  
  858. static void
  859. addtohistory(msgid)
  860. char *msgid;
  861. {
  862.     long historyfpos;
  863.     int duphandle;
  864.  
  865.     addtohashtable(msgid);
  866.  
  867.     historyfpos = ftell(nntp->historyf);
  868.     fseek(nntp->historyf,0L,SEEK_END);
  869.     fputs(msgid,nntp->historyf);
  870.     fflush(nntp->historyf);
  871.     if (nntpsafety) {
  872.         duphandle = dup(fileno(nntp->historyf));
  873.         close(duphandle);
  874.     }
  875.     fseek(nntp->historyf,historyfpos,SEEK_SET);
  876.     nntp->historyentries++;
  877. }
  878.  
  879.  
  880. /*
  881. *    stripgroups reformats a group list in situ for a NEWNEWS request
  882. *
  883. *    returns -1 if there is leading space,
  884. *                0 if line is blank otherwise
  885. *                n the number of characters in the resultant string
  886. */
  887.  
  888. static int
  889. stripgroups(s)
  890. char *s;
  891. {
  892.     char *d = s;
  893.     int count = 0;
  894.  
  895.     if (!isspace(*s)) return -1;
  896.  
  897.     do s++; while (*s == ',' || isspace(*s));
  898.  
  899.     while (*s != '\0') {
  900.         if (count != 0) {*d++ = ','; count++;}
  901.         do {
  902.             *d++ = *s++; count++;
  903.         } while (*s != '\0' && *s != ',' && !isspace(*s));
  904.         while (*s == ',' || isspace(*s)) s++;
  905.     }
  906.     *d = '\0';
  907.     return count;
  908. }
  909.  
  910.  
  911. static unsigned long
  912. unitspersecond(units,milliseconds)
  913. unsigned long units,milliseconds;
  914. {
  915.     if (milliseconds == 0) { /* Avoid divide-by-zero */
  916.         return 0;
  917.     } else {
  918.         if(units < 4294967UL) {
  919.             return (units*1000UL)/milliseconds;
  920.         } else {    /* Avoid overflow */
  921.             return units/(milliseconds/1000UL);
  922.         }
  923.     }
  924. }
  925.  
  926. /*
  927.     getreply - get reply from nntp server
  928.              - trace responses at level 3 or higher
  929. */
  930.     
  931. static int getreply(void)
  932. {
  933.     int n, response;
  934.     char buf[NNTPMAXLEN];
  935.  
  936.     while ((n = recvline(nntp->s, buf, NNTPMAXLEN)) != -1) {
  937.         nntp->receivedtotalchars += n;
  938.         
  939.         if (nntptrace >= 3)                        /* write trace message ?        */
  940.             tprintf("< %s", buf);
  941.             
  942.         /* skip informative messages and blank lines */
  943.  
  944.         if(buf[0] == '\n' || buf[0] == '1')
  945.             continue;
  946.  
  947.         sscanf(buf, "%d", &response);
  948.  
  949.         if (response >= 400) {
  950.             error (1, buf);            /* display error message */
  951.         }
  952.         return response;
  953.     }
  954.     return -1;
  955. }
  956.  
  957.  
  958. static int
  959. getheader(request)
  960. struct nntprequest *request;
  961. {
  962.     char buf[NNTPMAXLEN];
  963.     int n, nlines = 0;
  964.     unsigned long nchars = 0L;
  965.     struct textline *newline, *lastheaderline = NULLTEXTLINE;
  966.     unsigned long endtime, starttime = msclock();
  967.  
  968.     while ((n = recvline(nntp->s,buf,NNTPMAXLEN)) != -1) {
  969.  
  970.         nntp->receivedtotalchars += n;
  971.         nchars += n;
  972.  
  973.         if (nntptrace >= 4)
  974.             tprintf("<==%s", buf);
  975.  
  976.         if(strcmp(buf,".\n") == 0) {
  977.             request->lines += nlines;
  978.             if (nntptrace >= 3) {
  979.                 endtime = msclock();
  980.                 tprintf("<--%d lines, %lu chars, %lu sec (%lu bytes/sec)\n",
  981.                     nlines,
  982.                     nchars,
  983.                     (endtime-starttime)/1000UL,
  984.                     unitspersecond(nchars,endtime-starttime));
  985.             }
  986.             /* default to getting the body */
  987.             if (request->status != reqkill)
  988.                 request->status = reqkeep;
  989.             return nlines;
  990.         }
  991.  
  992.         nlines++;
  993.  
  994.         /* check for escaped '.' characters */
  995. #if 0
  996.         if(strcmp(buf,"..\n") == 0) {
  997.             buf[1] = '\n';
  998.             buf[2] = '\0';
  999.             n--;
  1000.         }
  1001. #endif
  1002.  
  1003.         if (*buf == '.') {
  1004.             memmove(buf, buf + 1, n);
  1005.             n--;
  1006.         }
  1007.  
  1008.         request->chars += n;
  1009.  
  1010.         if ((newline = (struct textline *) malloc(MINTEXTLINESIZE+n+1)) == NULLTEXTLINE) {
  1011.             error(2,"NNTP Can't allocate header buffer");
  1012.             return -1;
  1013.         }
  1014.         newline->nextline = NULLTEXTLINE;
  1015.         memcpy(newline->text,buf,n+1);
  1016.  
  1017.         if (lastheaderline == NULLTEXTLINE)
  1018.             request->header = newline;
  1019.         else
  1020.             lastheaderline->nextline = newline;
  1021.         lastheaderline = newline;
  1022.  
  1023.         if (request->status != reqkeep)
  1024.             scankilllists(buf,(int *) &(request->status));
  1025.     }
  1026.     sprintf(buf,"NNTP Receive error after %d lines", nlines);
  1027.     error(2,buf);
  1028.     return -1;
  1029. }
  1030.  
  1031.  
  1032. static void
  1033. moveheadertofile(fp,request)
  1034. FILE *fp;
  1035. struct nntprequest *request;
  1036. {
  1037.     struct textline *doneline, *thisline = request->header;
  1038.  
  1039.     while (thisline != NULLTEXTLINE) {
  1040.         fputs(thisline->text,fp);
  1041.         doneline = thisline;
  1042.         thisline = thisline->nextline;
  1043.         free(doneline);
  1044.     }
  1045.     request->header = NULLTEXTLINE;
  1046. }
  1047.  
  1048.  
  1049. static int gettxt(FILE *fp, struct nntprequest *request, int newnews)
  1050. {
  1051.     char buf[NNTPMAXLEN];
  1052.     int n, nlines = 0;
  1053.     unsigned long nchars = 0L;
  1054.     unsigned long endtime, starttime = msclock();
  1055.  
  1056.     while ((n = recvline(nntp->s,buf,NNTPMAXLEN)) != -1) {
  1057.  
  1058.         nntp->receivedtotalchars += n;
  1059.         nchars += n;
  1060.  
  1061.         if (nntptrace >= 4)
  1062.             tprintf("<- %s", buf);
  1063.  
  1064.         if(strcmp(buf,".\n") == 0) {
  1065.             if (nntptrace < 4 && (newnews != 0 && nntpverbose != 0))    {
  1066.                 tprintf ("\n");
  1067.             }
  1068.  
  1069.             if (request != NULLNNTPREQUEST)
  1070.                 request->lines += nlines;
  1071.             if (nntptrace >= 3) {
  1072.                 endtime = msclock();
  1073.                 tprintf("<--%d lines, %lu chars, %lu sec (%lu bytes/sec)\n",
  1074.                     nlines,
  1075.                     nchars,
  1076.                     (endtime-starttime)/1000UL,
  1077.                     unitspersecond(nchars,endtime-starttime));
  1078.             }
  1079.             return nlines;
  1080.         }
  1081.  
  1082.         nlines++;
  1083.         if (nntptrace < 4 && (newnews != 0 && nntpverbose != 0))
  1084.             tprintf ("\rNews header %d received ", nlines);
  1085.  
  1086.         /* check for escaped '.' characters */
  1087. #if 0        
  1088.         if(strcmp(buf,"..\n") == 0) {
  1089.             buf[1] = '\n';
  1090.             buf[2] = '\0';
  1091.             n--;
  1092.         }
  1093. #endif
  1094.  
  1095.         if (*buf == '.') {
  1096.             (void)memmove(buf, buf + 1, n);
  1097.             n--;
  1098.         }
  1099.  
  1100.         if (request != NULLNNTPREQUEST)
  1101.             request->chars += n;
  1102.  
  1103.         (void) fputs(buf,fp);
  1104.     }
  1105.  
  1106.     if (nntptrace < 4 && (newnews != 0 && nntpverbose != 0))
  1107.         tprintf ("\n");
  1108.     
  1109.     sprintf(buf,"NNTP Receive error after %d lines", nlines);
  1110.     error(2,buf);
  1111.     return -1;
  1112. }
  1113.  
  1114.  
  1115. static int
  1116. putarticle(msgf,msgfpos,request)
  1117. FILE *msgf;
  1118. long int *msgfpos;
  1119. struct nntprequest *request;
  1120. {
  1121.     char buf[NNTPMAXLEN];
  1122.     int duphandle;
  1123.  
  1124.     if (EOF == fflush(msgf))  {
  1125.         error(1, "NNTP Error writing newsbatch file");
  1126.         return -1;
  1127.     }
  1128.  
  1129.     (void) fseek(msgf,*msgfpos,SEEK_SET);
  1130.     fprintf(msgf,NEWSBATCHFORMAT,request->chars);
  1131.     if (EOF == fflush(msgf))  {
  1132.         error(1, "NNTP Error writing newsbatch file");
  1133.         return -1;
  1134.     }
  1135.  
  1136.     if (nntpsafety) {
  1137.         duphandle = dup(fileno(msgf));
  1138.         close(duphandle);
  1139.     }
  1140.  
  1141.     if (nntpverbose) {
  1142.         (void) fseek(msgf,*msgfpos,SEEK_SET);
  1143.         while(fgets(buf,NNTPMAXLEN,msgf) != NULLCHAR)
  1144.             if(buf[0] == '\n' || strnicmp(buf,"Newsgroups: ",12) == 0)
  1145.                 break;
  1146.         if (strnicmp(buf,"Newsgroups: ",12) == 0)
  1147.             rip(buf+12);
  1148.         else
  1149.             buf[12] = '\0';
  1150.         tprintf("News arrived (%u/%u): %s, %s",
  1151.             nntp->getreceived + nntp->newheadersreceived + nntp->newreceived + 1,
  1152.             (nntp->getarticles - nntp->getinvalid - nntp->getunavailable) + 
  1153.                 (nntp->newarticles - nntp->newduplicates - nntp->newunavailable),
  1154. /*            buf+12, */ buf,
  1155.             request->msgid);
  1156.     }
  1157.  
  1158.     /* set new length */
  1159.     (void) fseek(msgf,0L,SEEK_END);
  1160.     *msgfpos = ftell(msgf);
  1161.     fprintf(msgf,NEWSBATCHFORMAT,0UL);
  1162.  
  1163.     return 0;
  1164. }
  1165.  
  1166.  
  1167.  
  1168. static void __stdargs
  1169. nntp_init(argc,argv,parentproc)
  1170. int argc;
  1171. void *argv,*parentproc;
  1172. {
  1173.     createhashtable();
  1174.     createkilllists();    
  1175.  
  1176.     psignal(parentproc,0);
  1177.     nntp->childproc = NULLPROC;
  1178. }
  1179.  
  1180.  
  1181. static void __stdargs
  1182. nntp_send(argc,argv,parentproc)
  1183. int argc;
  1184. void *argv,*parentproc;
  1185. {
  1186.     int quit = FALSE;
  1187.  
  1188.     if (nntp->getmsgf != NULLFILE)
  1189.         rewind(nntp->getmsgf);
  1190.     rewind(nntp->newmsgf);
  1191.  
  1192.     while (!quit) {
  1193.  
  1194.         switch (nntp->requesthead->status) {
  1195.  
  1196.             case reqempty:
  1197.                 if (nntp->getmsgf != NULLFILE &&
  1198.                         fgets(nntp->requesthead->msgid,NNTPMAXLEN,nntp->getmsgf) != NULLCHAR) {
  1199.                     do { /* Find a line with a valid message id */
  1200.                         char *cp = nntp->requesthead->msgid;
  1201.                         nntp->getarticles++;
  1202.                         if (*cp++ != '<') {
  1203.                             nntp->getinvalid++;
  1204.                             if (!nntp->quit && !main_exit)
  1205.                                 pwait(NULL);
  1206.                             continue;
  1207.                         }
  1208.                         while (*cp != '>' && isgraph(*cp))
  1209.                             cp++;
  1210.                         if (*cp++ == '>') {
  1211.                             *cp++ = '\n';
  1212.                             *cp = '\0';
  1213.                             nntp->requesthead->status = reqgetarticle;
  1214.                             break;
  1215.                         }
  1216.                         nntp->getinvalid++;
  1217.                         if (!nntp->quit && !main_exit)
  1218.                             pwait(NULL);
  1219.                     } while (fgets(nntp->requesthead->msgid,NNTPMAXLEN,nntp->getmsgf) != NULLCHAR);
  1220.                 }
  1221.                 if (nntp->requesthead->status == reqempty &&
  1222.                         fgets(nntp->requesthead->msgid,NNTPMAXLEN,nntp->newmsgf) != NULLCHAR) {
  1223.                     do {
  1224.                         if (!isinhistory(nntp->requesthead->msgid)) {
  1225.                             if (nntp->killlist == NULLTEXTLINE)
  1226.                                 nntp->requesthead->status = reqnewarticle;
  1227.                             else
  1228.                                 nntp->requesthead->status = reqheader;
  1229.                             break;
  1230.                         }
  1231.                         if (nntpverbose)
  1232.                             tprintf("News duplicate: article %s",nntp->requesthead->msgid);
  1233.                         nntp->newduplicates++;
  1234.                         if (!nntp->quit && !main_exit)
  1235.                             pwait(NULL);
  1236.                     } while (fgets(nntp->requesthead->msgid,NNTPMAXLEN,nntp->newmsgf) != NULLCHAR);
  1237.                 }
  1238.                 if (nntp->requesthead->status == reqempty)
  1239.                     nntp->requesthead->status = reqend;
  1240.                 break;
  1241.  
  1242.             case reqkeep:
  1243.                 nntp->requesthead->status = reqbody;
  1244.                 break;
  1245.  
  1246.             case reqend:
  1247.                 nntp->requesthead->status = reqquit;
  1248.                 break;
  1249.  
  1250.             default: /* should never get here */
  1251.                 error(2,"NNTP Send internal error 1");
  1252.                 nntp->requesthead->status = reqquit;
  1253.         }
  1254.  
  1255.         if (!nntpbatch && nntp->requesthead != nntp->requesttail)
  1256.             pwait(Curproc);
  1257.  
  1258.         if (nntp->quit || main_exit) {
  1259.             psignal(parentproc,0);
  1260.             break;
  1261.         }
  1262.  
  1263.         switch (nntp->requesthead->status) {
  1264.             case reqend:
  1265.                 break;
  1266.             case reqquit:
  1267.                 if (nntptrace >= 3)
  1268.                     tprintf("==>QUIT\n");
  1269.                 usprintf(nntp->s,"QUIT\n");
  1270.                 quit = TRUE;
  1271.                 break;
  1272.             case reqgetarticle:
  1273.             case reqnewarticle:
  1274.                 if (nntptrace >= 3)
  1275.                     tprintf("==>ARTICLE %s",nntp->requesthead->msgid);
  1276.                 usprintf(nntp->s,"ARTICLE %s",nntp->requesthead->msgid);
  1277.                 break;
  1278.             case reqheader:
  1279.                 if (nntptrace >= 3)
  1280.                     tprintf("==>HEAD %s",nntp->requesthead->msgid);
  1281.                 usprintf(nntp->s,"HEAD %s",nntp->requesthead->msgid);
  1282.                 break;
  1283.             case reqbody:
  1284.                 if (nntptrace >= 3)
  1285.                     tprintf("==>BODY %s",nntp->requesthead->msgid);
  1286.                 usprintf(nntp->s,"BODY %s",nntp->requesthead->msgid);
  1287.                 break;
  1288.             default: /* should never get here */
  1289.                 error(2,"NNTP Send internal error 2");
  1290.                 if (nntptrace >= 3)
  1291.                     tprintf("==>QUIT\n");
  1292.                 usprintf(nntp->s,"QUIT\n");
  1293.                 quit = TRUE;
  1294.         }
  1295.  
  1296.         if (nntpbatch && nntp->requesthead->nextrequest == nntp->requesttail)
  1297.             pwait(Curproc);
  1298.  
  1299.         nntp->requesthead = nntp->requesthead->nextrequest;
  1300.         psignal(parentproc,0);
  1301.  
  1302.     }
  1303.  
  1304.     /* let the folks know we've gone */
  1305.     nntp->childproc = NULLPROC;
  1306. }
  1307.  
  1308. /*
  1309.     nntpmain - nntp main routine
  1310. */
  1311.  
  1312. static void __stdargs nntp_main(int i1, void *tp, void *v1)
  1313. {
  1314.     int reply, lines, i;
  1315.     char buf1[NNTPMAXLEN], buf2[NNTPMAXLEN], *cp;
  1316.  
  1317.     struct nntpservers *np = (struct nntpservers *)tp;
  1318.     struct sockaddr_in fsocket;
  1319.  
  1320.     int     post_ok;                                    /* code to post via NNTP - ATARI    */
  1321.     char     *cp1;
  1322.     char    nnfix[9]; 
  1323.     char    wgroup[80];
  1324.     char    wmsgid[80];
  1325.    char    atmp[LINELEN];
  1326.    char    wtmp[LINELEN];
  1327.     char    ttmp[LINELEN];
  1328.     char    wfilename[13];
  1329.     FILE    *wfile;
  1330.     FILE    *tfile;
  1331.     
  1332.    FILE *nntp_dat;                                /* handle for nntp.dat                */
  1333.     FILE *tmpf1, *tmpf2, *gettmpf, *msgf = NULLFILE;
  1334.     long int tmpfpos, msgfpos;
  1335.     char datenow[14], lastdate[14];
  1336.  
  1337.     if (nntptrace >= 3)
  1338.         tprintf("NNTP daemon entered, target = %s\n", np->name);
  1339.  
  1340.     if (nntp != NULLNNTPSESSION) {             /* nntp already active                 */
  1341.         error(2, "NNTP Already active");
  1342.         start_timer(&np->nntpcli_t);
  1343.         return;
  1344.     }
  1345.  
  1346.     { /* check the time - keep a record for updating the data file */
  1347.     
  1348.         time_t t;
  1349.         struct tm *ltm;
  1350.         int now;
  1351.  
  1352.         time(&t);                                    /* more portable than gettime() */
  1353.         t -= 120L;                                    /* 2 minute fudge factor            */
  1354.         ltm = localtime(&t);
  1355.         now = ltm->tm_hour * 100 + ltm->tm_min;
  1356.  
  1357.         if ((np->lowtime < np->hightime) ?     /* doesn't cross midnight */
  1358.             (now < np->lowtime || now >= np->hightime) :
  1359.             (now < np->lowtime && now >= np->hightime)) {
  1360.                 sprintf(buf2, "NNTP Window to '%s' not open", np->name);
  1361.                 error(2, buf2);
  1362.                 start_timer(&np->nntpcli_t);
  1363.                 return;
  1364.         }
  1365.  
  1366.         ltm = gmtime(&t);
  1367.         sprintf(datenow, "%02d%02d%02d %02d%02d%02d", ltm->tm_year%100,
  1368.             ltm->tm_mon+1, ltm->tm_mday, ltm->tm_hour, ltm->tm_min, ltm->tm_sec);
  1369.     }
  1370.  
  1371.     if (availmem() < Memthresh+sizeof(struct nntpsession) ||
  1372.         (nntp = (struct nntpsession *)calloc(1, sizeof(struct nntpsession))) == NULLNNTPSESSION) {
  1373.         error(2, "NNTP Memory too low");
  1374.         start_timer(&np->nntpcli_t);            /* restart nntp ick timer            */
  1375.         return;
  1376.     }
  1377.     nntp->s = -1;
  1378.  
  1379.     if (nntpverbose)
  1380.         tprintf("Trying to connect to news server %s ....\n",np->name);
  1381.  
  1382.     /* set up a circular list of request buffers                                     */
  1383.     
  1384.     {
  1385.         struct nntprequest *head, *tail;
  1386.  
  1387.         if (availmem() < Memthresh+sizeof(struct nntprequest) ||
  1388.             (head = tail = 
  1389.                 (struct nntprequest *)calloc(1, sizeof(struct nntprequest))) == NULLNNTPREQUEST) {
  1390.             error(2, "NNTP Memory too low");
  1391.             goto quit;
  1392.         }
  1393.         
  1394.         for (i = 1; i < nntpbatchsize || i < 2; i++) {
  1395.             if (availmem() < Memthresh+sizeof(struct nntprequest) ||
  1396.                 (tail->nextrequest =
  1397.                     (struct nntprequest *)calloc(1, sizeof(struct nntprequest))) == NULLNNTPREQUEST) {
  1398.                 error(2, "NNTP Memory too low");
  1399.                 goto quit;
  1400.             }
  1401.             tail = tail->nextrequest;
  1402.         }
  1403.         
  1404.         nntp->requesttail = nntp->requesthead = tail->nextrequest = head;
  1405.     }
  1406.  
  1407.  
  1408.     /* Find the server and try to connect                                                 */
  1409.     
  1410.     fsocket.sin_addr.s_addr = resolve(np->name);
  1411.  
  1412.     if(fsocket.sin_addr.s_addr == 0) {      /* No IP address found                 */
  1413.         sprintf(buf2, "NNTP Can't resolve host: %s", np->name);
  1414.         error(2, buf2);
  1415.         goto quit;
  1416.     }
  1417.     
  1418.     fsocket.sin_family = AF_INET;                /* setup socket                        */
  1419.     fsocket.sin_port      = IPPORT_NNTP;
  1420.     nntp->s = socket(AF_INET,SOCK_STREAM,0);
  1421.     sockmode(nntp->s,SOCK_ASCII);
  1422.  
  1423.     alarm (connect_wait_val);                    /* connection timeout alarm        */
  1424.     
  1425.     if    (connect(nntp->s,    (char *)&fsocket,SOCKSIZE) == -1) {
  1426.         if (!run_timer(&Curproc->alarm))
  1427.             sprintf(buf2, "NNTP Connect failed: timeout");
  1428.         else {
  1429.             alarm (0L);                                /* reset alarm                            */
  1430.             cp = sockerr(nntp->s);
  1431.             sprintf(buf2, "NNTP Connect failed: %s", cp != NULLCHAR ? cp : "reason unknown");
  1432.         }
  1433.         error(2, buf2);
  1434.         goto quit;                                    /* quit - connect failed connect */
  1435.     }
  1436.     
  1437.     alarm (0L);                                        /* reset alarm                            */
  1438.  
  1439.     nntp->startsession = msclock();            /* store session start time        */
  1440.     nntp->startarticles = 0;                    /* zero article start time            */
  1441.  
  1442.     if (nntpverbose)
  1443.         tprintf("Connected to news server\n");
  1444.  
  1445.     /* Open the history file                                                                 */
  1446.     
  1447.     sprintf(buf1, "%s/history", Newsdir);
  1448.     if (mlock(Newsdir, "history")) {
  1449.         sprintf(buf2, "NNTP Can't lock file %s", buf1);
  1450.         error(2, buf2);
  1451.         goto quit;                                     /* quit -can't lock history file    */
  1452.     }
  1453.     
  1454.     if ((nntp->historyf = fopen(buf1, APPEND_TEXT)) == NULLFILE) {
  1455.         sprintf(buf2, "NNTP Can't open file %s", buf1);
  1456.         error(1, buf2);
  1457.         rmlock(Newsdir, "history");
  1458.         goto quit;                                    /* quit - can't open history         */
  1459.     }
  1460.  
  1461.     /* spawn a process to build the internal tables                                 */
  1462.  
  1463.     nntp->childproc = newproc("NNTP client init", 1024, nntp_init, 0, NULL, Curproc, 0);
  1464.  
  1465.     reply = getreply();                            /* get banner from nntp server    */ 
  1466.  
  1467.     if(!(reply == 200 || reply == 201)) { 
  1468.         sprintf(buf2, "NNTP Bad reply on banner (response was %d)", reply);
  1469.         error(1, buf2);
  1470.         goto quit;                                    /* quit - bad banner                    */
  1471.     }
  1472.  
  1473. /* --- ATARI Code to support posting of Articles - from ANOS NNTP ---------*/
  1474.  
  1475.     if (reply == 201) {
  1476.        if (nntptrace >= 1)
  1477.           tprintf("NNTP - No posting allowed on '%s'\n", np->name);
  1478.     }
  1479.     else {
  1480.         for (filedir(Newsqueue, 0, wfilename);
  1481.               wfilename[0] != '\0';
  1482.             filedir(Newsqueue, 1, wfilename)) {
  1483.  
  1484.           cp  = wfilename;
  1485.           cp1 = nnfix;
  1486.  
  1487.           while (*cp && *cp != '.')
  1488.              *cp1++ = *cp++;
  1489.           *cp1 = '\0';
  1490.  
  1491.           sprintf(wtmp, "%s/%s", Newsqdir, wfilename);
  1492.  
  1493.           if ((wfile = fopen(wtmp,READ_TEXT)) == NULLFILE) 
  1494.              continue;
  1495.           (void) fgets(wmsgid,LINELEN,wfile);      /* read messageid */
  1496.           rip(wmsgid);
  1497.  
  1498. /* Validate the target groups    - post if 1 or more is valid                        */
  1499.    
  1500.           post_ok = FALSE;
  1501.  
  1502.           while (fgets(wgroup, LINELEN, wfile)) { /* read target group         */
  1503.               rip(wgroup);
  1504.               
  1505.               if (nntptrace >= 3)                /* write trace message ?            */
  1506.                  tprintf("> GROUP %s\n", wgroup);
  1507.                  
  1508.               usprintf(nntp->s, "GROUP %s\n", wgroup); /* send query            */
  1509.               reply = getreply();                /* get reply from server            */
  1510.               
  1511.               if (reply == 411) {
  1512.                  if (nntptrace >= 1)
  1513.                     tprintf("NNTP No Such Newsgroup '%s'\n", wgroup);
  1514.                  continue;                        /* try next group in the list        */
  1515.              }
  1516.              
  1517.              if (reply == 211)
  1518.                  post_ok = TRUE;                /* newsgroup OK                        */
  1519.             }
  1520.  
  1521.           fclose(wfile);
  1522.  
  1523.           if (post_ok) {                            /* post if 1 or more valid groups*/
  1524.           
  1525.              if (nntptrace >= 3)                /* write trace message ?            */
  1526.                 tprintf("> IHAVE %s \n", wmsgid);
  1527.                 
  1528.              usprintf(nntp->s, "IHAVE %s\n", wmsgid);    /* offer item             */
  1529.              reply = getreply();                /* get reply from server            */
  1530.  
  1531.              if (reply == 335) {                /* send new article                    */
  1532.                 sprintf(atmp, "%s/%s.txt", Newsqdir, nnfix);
  1533.  
  1534.                 if ((tfile = fopen(atmp, READ_TEXT)) != NULLFILE) {
  1535.  
  1536.                         while(fgets(ttmp, LINELEN, tfile) != NULLCHAR) {
  1537.                        if (nntptrace >= 4)    /* write trace message ?            */ 
  1538.                            tprintf("> ttmp");
  1539.                       usprintf(nntp->s,ttmp);        /* send line of article        */
  1540.                   }
  1541.                       
  1542.                         usprintf(nntp->s, ".\n");        /* send end-of-text marker    */
  1543.                    fclose(tfile);
  1544.                 }
  1545.  
  1546.                     reply = getreply();                    /* article posted, get rep    */
  1547.  
  1548.                     if(reply == 235) {                    /* posting OK ?                */
  1549.                    remove(atmp);                        /* remove article and        */
  1550.                    remove(wtmp);                        /*        work files            */
  1551.                 }
  1552.              }
  1553.              
  1554.              if(reply == 436)
  1555.                 if (nntptrace >= 1)
  1556.                    tprintf("NNTP '%s' Transfer Failed - Try later\n", wmsgid);
  1557.                    
  1558.              if(reply == 435) {
  1559.                 if (nntptrace >= 1)
  1560.                    tprintf("NNTP Article '%s' Not wanted\n", wmsgid);
  1561.                 remove(wtmp);
  1562.              }
  1563.  
  1564.              if(reply == 437) {
  1565.                 if (nntptrace >= 1)
  1566.                    tprintf("NNTP Article '%s' Rejected\n", wmsgid);
  1567.                 remove(wtmp);
  1568.              }
  1569.           }
  1570.        }
  1571.     }
  1572.  
  1573.     /*----------------------------------------------------------------------*/
  1574.     /* Now fetch new messages                                                                */
  1575.     /*----------------------------------------------------------------------*/
  1576.  
  1577.     /* Open nntp.dat                                                                             */
  1578.     
  1579.     sprintf(buf1, "%s/nntp.dat", Newsdir);
  1580.     if (mlock(Newsdir, "nntp")) {
  1581.         sprintf(buf2, "NNTP Can't lock file %s", buf1);
  1582.         error(2, buf2);
  1583.         goto quit;                                    /* quit, can't lock nntp.dat        */
  1584.     }
  1585.     
  1586.     if ((nntp_dat = fopen(buf1, APPEND_TEXT)) == NULLFILE) {
  1587.         sprintf(buf2, "NNTP Can't open file %s", buf1);
  1588.         error(1, buf2);
  1589.         rmlock(Newsdir, "nntp");
  1590.         goto quit;                                    /* quit, cant' open nntp.dat        */
  1591.     }
  1592.  
  1593.     /* Open temporary file for message ids */
  1594.     
  1595.     if ((nntp->newmsgf = tmpfile()) == NULLFILE) {
  1596.         error(1, "NNTP Can't open temporary file for message ids");
  1597.         fclose(nntp_dat);
  1598.         rmlock(Newsdir,"nntp");
  1599.         goto quit;                                    /* quit, can't open msgid file    */
  1600.     }
  1601.  
  1602.     /* Look for a line in the data file with a matching server name             */
  1603.     
  1604.     rewind(nntp_dat);
  1605.     strcpy(lastdate, "700101 000000");
  1606.     i = strlen(np->name);
  1607.     cp = buf1 + i;
  1608.     
  1609.     while (fgets(buf1, NNTPMAXLEN, nntp_dat) != NULLCHAR) {
  1610.         if (strnicmp(buf1, np->name, i) == 0 && isspace(*cp)) {
  1611.             unsigned long date, time;
  1612.             if (sscanf(cp, " %6lu %6lu", &date, &time) == 2)
  1613.                 sprintf(lastdate, "%06lu %06lu", date, time);
  1614.             break;
  1615.         }
  1616.     }
  1617.  
  1618.     /* Look for a newsgroup list in the data file                                     */
  1619.     
  1620.     i = -1;
  1621.  
  1622.     while (fgets(buf2, NNTPMAXLEN, nntp_dat) != NULLCHAR)
  1623.         if ((i = stripgroups(buf2)) != 0)
  1624.             break;
  1625.  
  1626.     /* if there is a newsgroup list in the data file                                 */
  1627.     
  1628.     if (i > 0) {
  1629.         do {
  1630.             if (i > NNTPMAXGROUPLIST) {
  1631.                 error(1, "NNTP Newsgroup line too long");
  1632.                 fclose(nntp_dat);
  1633.                 rmlock(Newsdir, "nntp");        /* free lock on nntp.dat            */
  1634.                 goto quit;                            /* quit, NG line too long            */
  1635.             }
  1636.             
  1637.             strcpy(buf1, buf2);
  1638.             i = -1;
  1639.  
  1640.             /* add entries until no more entries, blank line or the list is full */
  1641.             
  1642.             while ((fgets(buf2, NNTPMAXLEN, nntp_dat) != NULLCHAR) &&
  1643.                     ((i = stripgroups(buf2)) > 0))
  1644.                 if (strlen(buf1)+i >= NNTPMAXGROUPLIST)
  1645.                     break;
  1646.                 else {
  1647.                     strcat(buf1,",");
  1648.                     strcat(buf1,buf2); i = -1;
  1649.                 }
  1650.  
  1651.             /* send the request and get the response */
  1652.             
  1653.             if (nntptrace >= 3)
  1654.                 tprintf("> NEWNEWS %s %s GMT\n", buf1, lastdate);
  1655. #if 0                
  1656.             nntp_reg();    
  1657. #endif
  1658.             usprintf(nntp->s, "NEWNEWS %s %s GMT\n", buf1, lastdate);
  1659.  
  1660.             if ((reply = getreply()) != 230 ||
  1661.                 (lines = gettxt(nntp->newmsgf, NULL, 1)) == -1) {
  1662.                 sprintf(buf2, "NNTP Error while getting NEWNEWS (response was %d)",
  1663.                           reply);
  1664.                 error(1, buf2);
  1665.                 fclose(nntp_dat);                    /* close nntp.dat                        */
  1666.                 rmlock(Newsdir, "nntp");        /* remove lock on nntp.dat            */
  1667.                 goto quit;                            /* quit, NEWNEWS error                */
  1668.             }
  1669.             
  1670.             nntp->newarticles += lines;
  1671.  
  1672.             /* if we're on a blank line then look for more newsgroup entries  */
  1673.             
  1674.             if (i == 0) {
  1675.                 i = -1;
  1676.                 while (fgets(buf2, NNTPMAXLEN, nntp_dat) != NULLCHAR)
  1677.                     if ((i = stripgroups(buf2)) != 0)
  1678.                         break;
  1679.             }
  1680.         } while (i > 0);
  1681.  
  1682.     } else { /* use the addservers entry, or the groups entry, or get everything (!) */
  1683.  
  1684.         if (nntptrace >= 3)
  1685.             tprintf("> NEWNEWS %s %s GMT\n",
  1686.                 np->groups ? np->groups : (Nntpgroups ? Nntpgroups : "*"),
  1687.                 lastdate);
  1688.                 
  1689.         usprintf(nntp->s, "NEWNEWS %s %s GMT\n",
  1690.             np->groups ? np->groups : (Nntpgroups ? Nntpgroups : "*"),
  1691.                 lastdate);
  1692.  
  1693.         if ((reply = getreply()) != 230 || (lines = gettxt(nntp->newmsgf,NULL, 1)) == -1) {
  1694.             sprintf(buf2,"NNTP Error while getting NEWNEWS (response was %d)",reply);
  1695.             error(1,buf2);
  1696.             fclose(nntp_dat);                        /* close nntp.dat                        */
  1697.             rmlock(Newsdir,"nntp");                /* remove lock on nntp.dat            */
  1698.             goto quit;                                /* quit, NEWNEWS error                */
  1699.         }
  1700.         
  1701.         nntp->newarticles = lines;
  1702.     }
  1703.  
  1704.     fclose(nntp_dat);                                /* close nntp.dat                        */
  1705.     rmlock(Newsdir,"nntp");                        /* remove lock on nntp.dat            */
  1706.  
  1707.     if (nntp->quit || main_exit)
  1708.         goto quit;
  1709.  
  1710.     if (nntpverbose && nntp->newarticles != 0)
  1711.         tprintf("News available: %d articles\n", nntp->newarticles);
  1712.  
  1713.     if (nntpnewgroups) {
  1714.  
  1715.         /* Open the newgroups file */
  1716.  
  1717.         sprintf(buf1, "%s/newgroup", Newsdir);
  1718.         if (mlock(Newsdir, "newgroup")) {
  1719.             sprintf(buf2, "NNTP Can't lock file %s", buf1);
  1720.             error(2, buf2);
  1721.             goto quit;                                /* quit, can't lock newgroups     */
  1722.         }
  1723.         
  1724.         if ((tmpf1 = fopen(buf1, APPEND_TEXT)) == NULLFILE) {
  1725.             sprintf(buf2, "NNTP Can't open file %s", buf1);
  1726.             error(1, buf2);
  1727.             rmlock(Newsdir, "newgroup");        /* remove lock on newgroup file  */
  1728.             goto quit;                                /* quit, can't open newgroup         */
  1729.         }
  1730.  
  1731.         if (nntptrace >= 3)                        /* write tracefile ?                    */
  1732.             tprintf("> NEWGROUPS %s GMT\n", lastdate);
  1733.             
  1734.         usprintf(nntp->s, "NEWGROUPS %s GMT\n", lastdate);
  1735.  
  1736.         if ((reply = getreply()) != 231 || (lines = gettxt(tmpf1,NULL, 0)) == -1) {
  1737.             sprintf(buf2,"NNTP Error while getting NEWGROUPS (response was %d)",reply);
  1738.             error(1,buf2);                            /* report error                        */
  1739.             fclose(tmpf1);                            /* close newgroups file                */
  1740.             rmlock(Newsdir,"newgroup");        /* remove lock on newgroups file */
  1741.             goto quit;                                /* quit, NEWGROUPS error            */
  1742.         }
  1743.  
  1744.         fflush(tmpf1);
  1745.         fseek(tmpf1, 0L, SEEK_END);
  1746.         tmpfpos = ftell(tmpf1);
  1747.         fclose(tmpf1);
  1748.         if (tmpfpos == 0L) {
  1749.             sprintf(buf1,"%s/newgroup",Newsdir);
  1750.             remove(buf1);
  1751.         }
  1752.         rmlock(Newsdir,"newgroup");            /* remove lock on newgroups file */
  1753.   
  1754.         nntp->groupsreceived = lines;
  1755.         
  1756.         if (nntpverbose && nntp->groupsreceived != 0)
  1757.             tprintf("News available: %d new groups\n", nntp->groupsreceived);
  1758.     }
  1759.  
  1760.  
  1761.     /* If it exists, open the get file */
  1762.     
  1763.     sprintf(buf1,"%s/get",Newsdir);
  1764.  
  1765.     if (access(buf1, 0) == 0) {
  1766.         if (mlock(Newsdir,"get")) {
  1767.             sprintf(buf2,"NNTP Can't lock file %s",buf1);
  1768.             error(2, buf2);
  1769.         } else {
  1770.             if ((nntp->getmsgf = fopen(buf1, READ_TEXT)) == NULLFILE) {
  1771.                 sprintf(buf2, "NNTP Can't open file %s", buf1);
  1772.                 error(1, buf2);
  1773.                 rmlock(Newsdir, "get");
  1774.             } else {
  1775.                 
  1776.                 /* Open temporary file for unavailable message ids */
  1777.  
  1778.                 sprintf(buf1, "%s/get.tmp", Newsdir);
  1779.  
  1780.                 if((gettmpf = fopen(buf1,WRITE_TEXT)) == NULLFILE) {
  1781.                     sprintf(buf2,"NNTP Can't open file %s",buf1);
  1782.                     error(1, buf2);
  1783.                     fclose(nntp->getmsgf);
  1784.                     nntp->getmsgf = NULLFILE;
  1785.                     rmlock(Newsdir, "get");
  1786.                 }
  1787.             }
  1788.         }
  1789.     }
  1790.  
  1791.     /* Open the batch file                                                                 */
  1792.         
  1793.     sprintf(buf1, "%s/batch.txt", News_spool ? News_spool : Mailspool);
  1794.     if (mlock(News_spool ? News_spool : Mailspool, "batch")) {
  1795.         sprintf(buf2, "NNTP Can't lock file %s", buf1);
  1796.         error(2, buf2);
  1797.         goto quit;                                /* quit, can't lock batch file    */
  1798.     }
  1799.         
  1800.     if((msgf = fopen(buf1, RW_LOOKUP_TEXT)) == NULLFILE &&
  1801.         (msgf = fopen(buf1, RW_CREATE_TEXT)) == NULLFILE) {
  1802.         sprintf(buf2, "NNTP Can't open file %s", buf1);
  1803.         error(1, buf2);
  1804.         rmlock(News_spool ? News_spool : Mailspool, "batch");
  1805.         goto quit;                                /* quit, can't open batch file    */
  1806.     }
  1807.         
  1808.     fseek(msgf, 0L, SEEK_END);
  1809.     msgfpos = ftell(msgf);
  1810.     fprintf(msgf, NEWSBATCHFORMAT, 0UL);
  1811.  
  1812.     /* wait for the tables to be built then start up the tx proc                 */
  1813.     
  1814.     if (nntp->childproc != NULLPROC)            /* wait for init task to finish    */
  1815.         if (pwait(Curproc) != 0)
  1816.             goto quit;
  1817.  
  1818.     if (nntp->quit || main_exit)
  1819.         goto quit;
  1820.  
  1821.     nntp->childproc = newproc("NNTP client send", 1024, nntp_send, 0, NULL, Curproc, 0);
  1822.  
  1823.     nntp->receivednewschars = nntp->receivedtotalchars;
  1824.     nntp->receivedheaderchars = nntp->receivedtotalchars;
  1825.     
  1826.     nntp->startarticles = msclock();
  1827.  
  1828.     while (!nntp->quit && !main_exit) {
  1829.  
  1830.         if (nntp->requesthead == nntp->requesttail) {
  1831.             if (nntp->childproc == NULLPROC) {
  1832.                 error(2, "NNTP Receive internal error");
  1833.                 goto quit;                            /* quit, internel receive error    */
  1834.             }
  1835.             
  1836.             if (pwait(Curproc) != 0)
  1837.                 goto quit;
  1838.         }
  1839.  
  1840.         if (nntp->requesttail->status != reqend) {
  1841.             reply = getreply();
  1842.  
  1843.             switch (reply) {
  1844.                 case 205:                             /* closing connection                 */
  1845.                     if (nntp->requesttail->status != reqquit) {
  1846.                         error(2, "NNTP Receive internal error (reply was 205)");
  1847.                         goto quit;                    /* quit, internel receive error    */
  1848.                     }
  1849.                     
  1850.                     sprintf(buf1, "%s/nntp.dat", Newsdir);
  1851.                     if (mlock(Newsdir, "nntp")) {
  1852.                         sprintf(buf2, "NNTP Can't lock file %s", buf1);
  1853.                         error(2, buf2);
  1854.                         goto quit;                    /* quit, can't lock nntp.dat        */
  1855.                     }
  1856.                     
  1857.                     if((nntp_dat = fopen(buf1, READ_TEXT)) == NULLFILE) {
  1858.                         sprintf(buf2, "NNTP Can't open file %s", buf1);
  1859.                         error(1, buf2);
  1860.                         rmlock(Newsdir, "nntp");    /* remove nntp.dat lockfile    */ 
  1861.                         goto quit;                    /* quit, can't open nntp.dat        */
  1862.                     }
  1863.                     
  1864.                     sprintf(buf1,"%s/nntp.tmp", Newsdir);
  1865.                     if((tmpf2 = fopen(buf1,WRITE_TEXT)) == NULLFILE) {
  1866.                         sprintf(buf2, "NNTP Can't open file %s", buf1);
  1867.                         error(1, buf2);
  1868.                         fclose(nntp_dat);                /* close nntp.dat                        */
  1869.                         rmlock(Newsdir, "nntp");    /* remove lock                        */
  1870.                         goto quit;                    /* quit, can't open nntp.tmp        */    
  1871.                     }
  1872.                     
  1873.                     sprintf(buf1, "%s %s\n", np->name, datenow);
  1874.                     i = strlen(np->name);
  1875.                     cp = buf2 + i;
  1876.                     
  1877.                     while (fgets(buf2, NNTPMAXLEN, nntp_dat) != NULLCHAR)
  1878.                         if (strnicmp(buf2,np->name,i) != 0 || !isspace(*cp))
  1879.                             fputs(buf2, tmpf2);
  1880.                         else {
  1881.                             fputs(buf1, tmpf2);
  1882.                             buf1[0] = '\0';
  1883.                         }
  1884.                     if (buf1[0] != '\0')
  1885.                         fputs(buf1,tmpf2);
  1886.                         fclose(nntp_dat);
  1887.                         sprintf(buf1, "%s/nntp.dat", Newsdir);
  1888.                         sprintf(buf2, "%s/nntp.tmp", Newsdir);
  1889.  
  1890.                     if (ferror(tmpf2)) {
  1891.                         fclose(tmpf2);
  1892.                         remove(buf2);
  1893.                         error(1, "Error writing new nntp.dat file");
  1894.                     } else {
  1895.                         fclose(tmpf2);
  1896.                         remove(buf1);
  1897.                         rename(buf2, buf1);
  1898.                     }
  1899.  
  1900.                     rmlock(Newsdir,"nntp");        /* remove lock on nntp.dat            */
  1901.                     goto quit;
  1902.  
  1903.  
  1904.                 case 220:                             /* header and body follows         */
  1905.                     if (nntp->requesttail->status != reqgetarticle &&
  1906.                         nntp->requesttail->status != reqnewarticle) {
  1907.                         error(2,    "NNTP Receive internal error (reply was 220)");
  1908.                         goto quit;                    /* quit, internel receive error    */
  1909.                     }
  1910.                     
  1911.                     nntp->requesttail->lines = 0;
  1912.                     nntp->requesttail->chars = 0UL;
  1913.  
  1914.                     if (gettxt(msgf, nntp->requesttail, 0) == -1)
  1915.                         goto quit;
  1916.                         
  1917.                     if (putarticle(msgf,&msgfpos,nntp->requesttail) == -1)
  1918.                         goto quit;
  1919.  
  1920.                     /*
  1921.                     *    The following is a compromise: if there is a hash
  1922.                     *    collision then the id will erroneously not get added.
  1923.                     *    However, scanning the history file here will cause
  1924.                     *    problems as the child might be doing the same.
  1925.                     */
  1926.  
  1927.                     if (nntp->requesttail->status == reqgetarticle) {
  1928.                         if (!isinhashtable(nntp->requesttail->msgid))
  1929.                             addtohistory(nntp->requesttail->msgid);
  1930.                         nntp->getreceived++;
  1931.                     } else {
  1932.                         addtohistory(nntp->requesttail->msgid);
  1933.                         nntp->newreceived++;
  1934.                     }
  1935.                     nntp->receivedarticlechars += nntp->requesttail->chars;
  1936.                     nntp->requesttail->status = reqempty;
  1937.                     break;
  1938.  
  1939.  
  1940.                 case 221:                             /* header follows                     */
  1941.                     if (nntp->requesttail->status != reqheader) {
  1942.                         error(2, "NNTP Receive internal error (reply was 221)");
  1943.                         goto quit;                    /* quit, internel receive error    */
  1944.                     }
  1945.                     
  1946.                     nntp->requesttail->lines = 0;
  1947.                     nntp->requesttail->chars = 0UL;
  1948.  
  1949.                     if (getheader(nntp->requesttail) == -1)
  1950.                         goto quit;
  1951.  
  1952.                     if (nntp->requesttail->status == reqkill) {
  1953.                         moveheadertofile(msgf,nntp->requesttail);
  1954.  
  1955.                         if (putarticle(msgf,&msgfpos,nntp->requesttail) == -1)
  1956.                             goto quit;
  1957.  
  1958.                         addtohistory(nntp->requesttail->msgid);
  1959.                         nntp->newheadersreceived++;
  1960.                         nntp->receivedarticlechars += nntp->requesttail->chars;
  1961.                         nntp->requesttail->status   = reqempty;
  1962.                     }
  1963.                     break;
  1964.  
  1965.                 case 222:                             /* body follows                         */
  1966.                     if (nntp->requesttail->status != reqbody) {
  1967.                         error(2, "NNTP Receive internal error (reply was 222)");
  1968.                         goto quit;                    /* quit, internal receive error    */
  1969.                     }
  1970.                     
  1971.                     moveheadertofile(msgf,nntp->requesttail);
  1972.                     fputc('\n', msgf);
  1973.                     nntp->requesttail->lines++;
  1974.                     nntp->requesttail->chars++;
  1975.  
  1976.                     if (gettxt(msgf,nntp->requesttail, 0) == -1)
  1977.                         goto quit;
  1978.  
  1979.                     if (putarticle(msgf,&msgfpos,nntp->requesttail) == -1)
  1980.                         goto quit;
  1981.  
  1982.                     addtohistory(nntp->requesttail->msgid);
  1983.                     nntp->newreceived++;
  1984.                     nntp->receivedarticlechars += nntp->requesttail->chars;
  1985.                     nntp->requesttail->status = reqempty;
  1986.                     break;
  1987.  
  1988.                 case 430:                             /* no such article                     */
  1989.                     if (nntp->requesttail->status != reqgetarticle &&
  1990.                          nntp->requesttail->status != reqnewarticle &&
  1991.                          nntp->requesttail->status != reqheader) {
  1992.                         error(2,"NNTP Receive internal error (reply was 430)");
  1993.                         goto quit;                    /* quit, internal receive error    */
  1994.                     }
  1995.                     
  1996.                     if (nntp->requesttail->status == reqgetarticle) {
  1997.                         fputs(nntp->requesttail->msgid,gettmpf);
  1998.                         nntp->getunavailable++;
  1999.                     } else
  2000.                         nntp->newunavailable++;
  2001.  
  2002.                     if (nntpverbose)
  2003.                         tprintf("News unavailable: article %s",nntp->requesttail->msgid);
  2004.  
  2005.                     nntp->requesttail->status = reqempty;
  2006.                     break;
  2007.  
  2008.                 case -1: /* error */
  2009.                     error(2, "NNTP Receive error");
  2010.                     goto quit;                        /* quit, NNTP receive error        */
  2011.  
  2012.  
  2013.                 default:
  2014.                     sprintf(buf2, "NNTP Unexpected reply from server (reply was %d)",reply);
  2015.                     error(2, buf2);
  2016.                     goto quit;                        /* quit, unexpected reply            */
  2017.             }
  2018.         }
  2019.  
  2020.         /* move on to next request                                           */
  2021.         
  2022.         nntp->requesttail = nntp->requesttail->nextrequest;
  2023.         psignal(nntp->childproc, 0);
  2024.     }
  2025.  
  2026.     /* quit nntp session                                                                        */
  2027.     
  2028. quit:
  2029.  
  2030.     nntp->endsession = msclock();
  2031.     nntp->receivednewschars = nntp->receivedtotalchars - nntp->receivednewschars;
  2032.  
  2033.     /* tidy up as necessary */
  2034.  
  2035.     if (msgf != NULLFILE) {
  2036.         fflush(msgf);                
  2037.         fclose(msgf);
  2038.         if (msgfpos == 0L) {
  2039.             sprintf(buf1, "%s/batch.txt",News_spool ? News_spool : Mailspool);
  2040.             (void) remove(buf1);
  2041.         }
  2042.         rmlock(News_spool ? News_spool : Mailspool, "batch");
  2043.     }
  2044.     
  2045.     else {
  2046.         if (msgf != NULLFILE) {
  2047.             fclose(msgf);
  2048.         }
  2049.     }
  2050.  
  2051.     nntp->quit = TRUE;
  2052.     if (nntp->childproc != NULLPROC)
  2053.         pwait(NULL);
  2054.     if (nntp->childproc != NULLPROC)
  2055.         killproc(nntp->childproc);
  2056.  
  2057.     /* Update get file with message ids we didn't get                                 */
  2058.     
  2059.     if (nntp->getmsgf != NULLFILE) {
  2060.         rewind(nntp->getmsgf);
  2061.  
  2062.         for (i = 0;
  2063.               i < (nntp->getinvalid + nntp->getunavailable + nntp->getreceived);
  2064.               i++)
  2065.             fgets(buf1, NNTPMAXLEN, nntp->getmsgf);
  2066.  
  2067.         while (fgets(buf1,NNTPMAXLEN,nntp->getmsgf) != NULLCHAR)
  2068.             fputs(buf1, gettmpf);
  2069.             
  2070.         fclose(nntp->getmsgf);
  2071.         sprintf(buf1, "%s/get", Newsdir);
  2072.         sprintf(buf2, "%s/get.tmp", Newsdir);
  2073.         
  2074.         if (ferror(gettmpf)) {
  2075.             fclose(gettmpf);
  2076.             remove(buf2);
  2077.             error(1,"Error writing new get file");
  2078.         } else {
  2079.             remove(buf1);
  2080.             fflush(gettmpf);
  2081.             fseek(gettmpf,0L,SEEK_END);
  2082.             tmpfpos = ftell(gettmpf);
  2083.             fclose(gettmpf);
  2084.  
  2085.             if (tmpfpos == 0L)
  2086.                 remove(buf2);
  2087.             else
  2088.                 rename(buf2, buf1);
  2089.         }
  2090.         rmlock(Newsdir,"get");
  2091.     }            
  2092.  
  2093.     if (nntp->newmsgf != NULLFILE)
  2094.         fclose(nntp->newmsgf);
  2095.  
  2096.     if (nntp->historyf != NULLFILE) {
  2097.         fclose(nntp->historyf);
  2098.         rmlock(Newsdir, "history");
  2099.     }
  2100.     if (nntp->hashtable != NULL)
  2101.         free(nntp->hashtable);
  2102.  
  2103.     /* Free all dynamically allocated buffer space */
  2104.     {
  2105.         struct nntprequest
  2106.             *tail = nntp->requesthead,
  2107.             *head = tail->nextrequest;
  2108.         tail->nextrequest = NULLNNTPREQUEST;
  2109.         while (head != NULLNNTPREQUEST) {
  2110.             freetextlinelist(&head->header);
  2111.             tail = head;
  2112.             head = head->nextrequest;
  2113.             free(tail);
  2114.         }
  2115.         freetextlinelist(&nntp->killlist);
  2116.         freetextlinelist(&nntp->keeplist);
  2117.     }
  2118.  
  2119.     /* Output statistics */
  2120.  
  2121.     if (nntp->getreceived != 0 || nntp->newheadersreceived != 0 || nntp->newreceived != 0) {
  2122.         sprintf(buf2,"News summary: %u articles (%lu bytes) in %lu sec (%lu bytes/sec)",
  2123.                 nntp->getreceived + nntp->newheadersreceived + nntp->newreceived,
  2124.                 nntp->receivedarticlechars,
  2125.                 (nntp->endsession - nntp->startarticles)/1000UL,
  2126.                 unitspersecond(nntp->receivedarticlechars,nntp->endsession - nntp->startarticles));
  2127.         log(nntp->s,buf2);
  2128.         tprintf(nntpverbose ? "\n%s\n" : "%s\n",buf2);
  2129.     }
  2130.  
  2131.     if (nntp->getarticles != 0) {
  2132.         sprintf(buf2,"Get articles: %u invalid, %u unavailable, %u received",
  2133.             nntp->getinvalid,
  2134.             nntp->getunavailable,
  2135.             nntp->getreceived);
  2136.         log(nntp->s,buf2);
  2137.         if (nntpverbose)
  2138.             tprintf("%s\n",buf2);
  2139.     }
  2140.  
  2141.     if (nntp->newarticles != 0) {
  2142.         sprintf(buf2,"New articles: %u duplicate, %u unavailable, %u headers, %u complete",
  2143.             nntp->newduplicates,
  2144.             nntp->newunavailable,
  2145.             nntp->newheadersreceived,
  2146.             nntp->newreceived);
  2147.         log(nntp->s,buf2);
  2148.         if (nntpverbose)
  2149.             tprintf("%s\n",buf2);
  2150.     }
  2151.  
  2152.     if (nntp->getreceived != 0 || nntp->newheadersreceived != 0 || nntp->newreceived != 0 ||
  2153.             nntp->getarticles != 0 || nntp->newarticles != 0) {
  2154.         sprintf(buf2,"History file: %u entries, %u complete scans",
  2155.             nntp->historyentries,
  2156.             nntp->historyscans);
  2157.         log(nntp->s,buf2);
  2158.         if (nntpverbose)
  2159.             tprintf("%s\n",buf2);
  2160.         sprintf(buf2,"Throughput  : %lu/%lu bytes in %lu/%lu sec (%lu/%lu bytes/sec)",
  2161.             nntp->receivednewschars,
  2162.             nntp->receivedtotalchars,
  2163.             (nntp->endsession - nntp->startarticles)/1000UL,
  2164.             (nntp->endsession - nntp->startsession)/1000UL,
  2165.             unitspersecond(nntp->receivednewschars,nntp->endsession - nntp->startarticles),
  2166.             unitspersecond(nntp->receivedtotalchars,nntp->endsession - nntp->startsession));
  2167.         log(nntp->s,buf2);
  2168.         if (nntpverbose)
  2169.             tprintf("%s\n\n",buf2);
  2170.     }
  2171.  
  2172.     if (nntpverbose)
  2173.         tprintf("Closing news session\n");
  2174.  
  2175.     close_s(nntp->s);
  2176.     free(nntp);
  2177.     nntp = NULLNNTPSESSION;
  2178.  
  2179.     if (nntptrace >= 3)
  2180.         tprintf("NNTP daemon exiting\n");
  2181.  
  2182.     /* Restart timer */
  2183.     start_timer(&np->nntpcli_t);
  2184.  
  2185.     return;
  2186. }
  2187.  
  2188. /*
  2189.     nntp_req
  2190. */
  2191.  
  2192. static void nntp_reg(void)
  2193. {
  2194.     struct sockaddr_in sock;
  2195.     struct socket lsocket;
  2196.     struct socket rsocket;
  2197.     struct usock *usp;
  2198.     struct sockaddr_in *sinp;
  2199.     struct mbuf *bp;
  2200.     char buf[64];
  2201.      int s;
  2202.  
  2203.     sock.sin_family = AF_INET;
  2204.     sock.sin_port = 2911;
  2205.     if ((sock.sin_addr.s_addr = resolve ("passwd.demon.co.uk")) == 0 ||
  2206.         (s = socket (AF_INET, SOCK_DGRAM, 0)) == -1 ||
  2207.         connect (s, (char *) &sock, sizeof (sock)) == -1)
  2208.         return;
  2209.  
  2210.     (void) sprintf (buf, "%s|%s|%s\n", Version, __DATE__, __TIME__);
  2211.     bp = ambufw (strlen (buf) + 1);
  2212.     (void) strcpy (bp->data, buf);
  2213.     usp = itop(s);
  2214.     sinp = (struct sockaddr_in *) usp->name;
  2215.     lsocket.address = sinp->sin_addr.s_addr;
  2216.     lsocket.port = sinp->sin_port;
  2217.     sinp = (struct sockaddr_in *)usp->peername;
  2218.     rsocket.address = sinp->sin_addr.s_addr;
  2219.     (void) send_udp (&lsocket, &rsocket, 0, 64, bp, bp->size, 0, 0);
  2220.     close_s (s);
  2221. }    
  2222.  
  2223.  
  2224. /*
  2225.     donnstats - Print NNTP statistics for current session
  2226. */
  2227.     
  2228. static int donnstatus(int argc, char *argv[], void *p)
  2229. {
  2230.     unsigned long timenow;
  2231.     
  2232.     if (nntp == NULLNNTPSESSION)  {
  2233.         tprintf("News: No session active \n");
  2234.         return 0;
  2235.     }
  2236.  
  2237.     timenow = msclock();
  2238.     
  2239.     if (nntp->startarticles == 0) {                /* Collecting headers            */
  2240.         tprintf("NNTP: Fetching new headers: Time %lu sec, Rate %lu chars/sec \n",
  2241.                 (timenow - nntp->startsession) / 1000UL,
  2242.                 unitspersecond(nntp->receivedtotalchars, timenow - nntp->startsession));
  2243. #if 0                
  2244.         tprintf("      %d headers received, %d duplicate, in %lu sec \n\n",
  2245.                 nntp->newarticles, nntp->newduplicates,
  2246.                 (timenow - nntp->startsession) / 1000UL );
  2247. #endif
  2248.         return 0;
  2249.     }
  2250.     
  2251.     tprintf("NNTP: Fetching articles: Time %lu sec, Rate %lu chars/sec\n",
  2252.                 (timenow - nntp->startarticles) / 1000UL,
  2253.                 unitspersecond(nntp->receivedtotalchars - nntp->receivedheaderchars,
  2254.                  timenow - nntp->startarticles ));
  2255.  
  2256.     tprintf("      %d headers received, %d duplicate, in %lu sec\n",
  2257.                 nntp->newarticles, nntp->newduplicates,
  2258.                 (nntp->startarticles - nntp->startsession) / 1000UL );
  2259.  
  2260.     if (nntp->getreceived > 0)
  2261.         tprintf("      %d/%d get articles received, %d invalid, %d unavailable \n",
  2262.                 nntp->getreceived,
  2263.                 nntp->getarticles - nntp->getinvalid - nntp->getunavailable,
  2264.                 nntp->getinvalid,
  2265.                 nntp->getunavailable);
  2266.  
  2267.     if (nntp->newreceived > 0)
  2268.         tprintf("      %d/%d new articles received, %d duplicates, %d unavailable \n",
  2269.                 nntp->newreceived,
  2270.                 nntp->newarticles - nntp->newduplicates - nntp->newunavailable,
  2271.                 nntp->newduplicates,
  2272.                 nntp->newunavailable);
  2273.                 
  2274. #if 0                 
  2275.     if (nntp->getreceived != 0 || nntp->newheadersreceived != 0 || nntp->newreceived != 0) {
  2276.         tprintf("\nNews summary: %u/%u articles received \n",
  2277.                 nntp->getreceived + nntp->newheadersreceived + nntp->newreceived,
  2278.                 (nntp->getarticles - nntp->getinvalid - nntp->getunavailable) + 
  2279.                 (nntp->newarticles - nntp->newduplicates - nntp->newunavailable));
  2280.     }
  2281.  
  2282.     if (nntp->getarticles != 0) {
  2283.         tprintf("Get articles: %u invalid, %u unavailable, %u received\n",
  2284.             nntp->getinvalid,
  2285.             nntp->getunavailable,
  2286.             nntp->getreceived);
  2287.     }
  2288.  
  2289.     if (nntp->newarticles != 0) {
  2290.         tprintf("New articles: %u duplicate, %u unavailable, %u headers, %u complete \n",
  2291.             nntp->newduplicates,
  2292.             nntp->newunavailable,
  2293.             nntp->newheadersreceived,
  2294.             nntp->newreceived);
  2295.     }
  2296. #endif
  2297.     if (nntp->getreceived != 0 || nntp->newheadersreceived != 0 || nntp->newreceived != 0 ||
  2298.             nntp->getarticles != 0 || nntp->newarticles != 0) {
  2299.         tprintf("      History file: %u entries, %u complete scans \n",
  2300.             nntp->historyentries,
  2301.             nntp->historyscans);
  2302.     }
  2303.     
  2304.     return 0;
  2305. }
  2306.  
  2307.  
  2308. /*
  2309.     donntplist - list messages wating to be sent
  2310. */
  2311.  
  2312. static int donntplist(int argc, char *argv[], void *p)
  2313. {
  2314.    char tstring[80], line[20],
  2315.         group[LINELEN], *cp;
  2316.    struct stat stbuf;
  2317.    struct tm *tminfo;
  2318.    FILE *fp;
  2319.  
  2320.    int    statx(const char *, struct stat *);
  2321.  
  2322.    Current->flowmode = 1;          /* Enable the more mechanism */
  2323.    tprintf("    Job    Size  Date  Time  Message ID\n");
  2324.    filedir(Newsqueue,0,line);
  2325.    while(line[0] != '\0') {
  2326.       sprintf(tstring,"%s/%s",Newsqdir,line);
  2327.       if ((fp = fopen(tstring,READ_TEXT)) == NULLFILE) {
  2328.          tprintf("Can't open %s: %s\n",tstring,strerror(errno));
  2329.          continue;
  2330.       }
  2331.       if ((cp = strrchr(line,'.')) != NULLCHAR)
  2332.          *cp = '\0';
  2333.       sprintf(tstring,"%s/%s.txt",Newsqdir,line);
  2334.       statx(tstring,&stbuf);                        /* call modified stat routine */
  2335.     
  2336.       tminfo = localtime(&stbuf.st_ctime);
  2337.       fgets(group,sizeof(group),fp);
  2338.       rip(group);
  2339.       tprintf("%7s %7ld  %02d/%02d %02d:%02d %-25s\n",
  2340.          line, stbuf.st_size,
  2341.          tminfo->tm_mon+1, tminfo->tm_mday, tminfo->tm_hour,
  2342.          tminfo->tm_min, group);
  2343.       (void) fclose(fp);
  2344.       pwait(NULL);
  2345.       filedir(Newsqueue,1,line);
  2346.    }
  2347.    Current->flowmode = 0;
  2348.    return 0;
  2349. }
  2350.  
  2351.  
  2352. /* kill a job in the mqueue */
  2353.  
  2354. static int donntpkill(int argc, char *argv[], void *p)
  2355. {
  2356.    char s[LINELEN];
  2357.    char *cp;
  2358.  
  2359.    sprintf(s,"%s/%s.nwk",Newsqdir,argv[1]);
  2360.    cp = strrchr(s,'.');
  2361.    if (remove(s))
  2362.       tprintf("Job id %s not found\n",argv[1]);
  2363.    strcpy(cp,".txt");
  2364.    (void) remove(s);
  2365.    return 0;
  2366. }
  2367.